import json
import getopt
import sys
import os
import subprocess


global end
end = False


def get_args(argv):
    # 命令行参数解析
    sys_args = {}
    try:
        opts, args = getopt.getopt(
            argv,
            "c:d:p:m:o:e:",
            [
                "sim-config=",
                "driver-config=",
                "param-valid-file=",
                "model-dir=",
                "output-file-path=",
                "entry=",
            ],
        )
    except getopt.GetoptError:
        print(
            "Error: matlabDriver.py -c <config.json> -d <driver.config.json>"
            "-p <param_valid.json> -m <model_dir> -o <output.json file path> -e <entry file name>"
        )
        print(
            "  or: aspenPlusDriver.py --sim-config=<config.json> --driver-config=<driver.config.json>"
            "--param-valid-file=<config.json> --model-dir=<model_dir> --output-file-path=<file path> "
            "--entry=<entry file name>"
        )
        sys.Exit(1)
    for opt, arg in opts:
        if opt in ("-c", "--sim-config"):
            sys_args["simConfig"] = arg
        elif opt in ("-d", "--driver-config"):
            sys_args["driverConfig"] = arg
        elif opt in ["-p", "--param-valid-file"]:
            sys_args["paramValidFile"] = arg
        elif opt in ["-m", "--mode-dir"]:
            sys_args["modelDir"] = arg
        elif opt in ["-o", "--output-file-path"]:
            sys_args["outputFilePath"] = arg
        elif opt in ["-e", "--entry"]:
            sys_args["entry"] = arg
    return sys_args


def writeOutputFile(output_file_path, content):
    if not os.path.isdir(os.path.dirname(output_file_path)):
        os.mkdir(os.path.dirname(output_file_path))
    with open(output_file_path, "w") as file:
        file.write(content)


def parse_param(model_dir, output_file_path, entry):
    tcl_path = os.path.join(model_dir, entry)
    result = []
    with open(tcl_path, "r") as file:
        lines = file.readlines()
    for index, line in enumerate(lines):
        if "# input #" in line:
            start_index = index
        elif "# end input #" in line:
            end_index = index
    for index, line in enumerate(lines):
        if line.startswith("set") and index > start_index and index < end_index:
            result.append(
                {
                    "name": line.split(" ")[1],
                    "value": line.split(" ")[2],
                    "type": "string",
                }
            )
    # hypermesh固定输出前处理文件
    output = [
        {
            "name": "bdf",
            "mdoLabel": "bdf",
            "value": "",
            "type": "string",
        },
        {
            "name": "cdb",
            "mdoLabel": "cdb",
            "value": "",
            "type": "string",
        },
        {
            "name": "inp",
            "mdoLabel": "inp",
            "value": "",
            "type": "string",
        },
    ]
    param_value = {"input": result, "output": output}
    writeOutputFile(
        output_file_path,
        json.dumps({"success": True, "message": "", "data": param_value, }),
    )


def run_hypermesh(cmd):
    print("simulation start", flush=True)
    p = subprocess.Popen(cmd)
    p.communicate()
    global end
    end = True
    return


def update_hm_tcl(path, parameters, label="# end input #"):
    # 更新输入参数值
    with open(path, "r") as file:
        lines = file.readlines()
    for i, line in enumerate(lines):
        if label in line:
            index = i
            add_lines = []
            for param in parameters:
                if param["type"] != "file":
                    add_lines.append(f"set {param['name']} {param['value']}\n")
                else:
                    value = param["value"].replace("\\", "/")
                    add_lines.append(f'''set {param['name']} "{value}"\n''')
            update_lines = lines[:index] + add_lines + lines[index:]
            with open(path, "w") as file:
                file.writelines(update_lines)
            break
    else:
        print("未找到输入参数标签")
        return


def export_hm_tcl(path, solver, hm_dir, work_dir):
    if solver == "bdf":
        nastran_dir = r"templates/feoutput/nastran/general"
        template_path = os.path.join(hm_dir, nastran_dir)
        filename = "output.bdf"
    elif solver == "cdb":
        ansys_dir = r"templates/feoutput/ansys/ansys.tpl"
        template_path = os.path.join(hm_dir, ansys_dir)
        filename = "output.cdb"
    elif solver == "inp":
        abaqus_dir = r"templates/feoutput/abaqus/standard.3d"
        template_path = os.path.join(hm_dir, abaqus_dir)
        filename = "output.inp"
    output_path = os.path.join(work_dir, filename)
    template_path = template_path.replace("\\", "/")
    output_path = output_path.replace("\\", "/")
    line = f'\n*feoutputwithdata "{template_path}" "{output_path}" 1 0 0 1 0'
    with open(path, "a") as file:
        file.write(line)
    # 添加求解器模板前缀
    pre = f'*templatefileset "{template_path}"'
    with open(path, "r+") as file:
        content = file.read()
        file.seek(0)
        file.write(f"{pre}\n" + content)


def run(run_config_json_file, driver_path, output_file):
    global end
    with open(driver_path, "r") as file:
        driver_config = json.load(file)
        hm_dir = driver_config["config"]["hypermeshDir"]
        exe_path = os.path.join(hm_dir, r"hm\bin\win64\hmbatch.exe")
    with open(run_config_json_file, "r") as file:
        simulation_config = json.load(file)
    absolute_entry = os.path.join(
        simulation_config["model"]["file"], simulation_config["model"]["entry"]
    )
    work_dir = os.path.dirname(absolute_entry)
    cmd = f"{exe_path} -tcl {absolute_entry}"
    # TODO 更新输入参数
    update_hm_tcl(absolute_entry, simulation_config["parameters"])
    for output in simulation_config["result"]["specifiedOutputParams"]:
        solver = output["name"]
        break
    export_hm_tcl(absolute_entry, solver, hm_dir, work_dir)
    run_hypermesh(cmd)
    while True:
        if end:
            params_value = {
                "success": True,
                "message": "",
                "data": {"simulationResultDir": "", "specifiedParameters": []},
            }
            print("simulation success")
            # 回传工程文件
            if simulation_config["result"]["uploadRawSimResult"]:
                params_value["data"]["simulationResultDir"] = work_dir
            # TODO 提取指定的输出参数
            output_param = simulation_config["result"]["specifiedOutputParams"][0]
            for file in os.listdir(work_dir):
                if file.endswith(output_param["name"]):
                    output_param["value"] = os.path.join(work_dir, file)
                    output_param["type"] = "file"
                    break
            params_value["data"]["specifiedParameters"].append(output_param)
            writeOutputFile(output_file, json.dumps(params_value))
            return


if __name__ == "__main__":
    mode = sys.argv[1]
    sysArgs = get_args(sys.argv[2:])
    print(f"mode = {mode},sysArgs = {sysArgs}", flush=True)
    if mode == "parse":
        # 参数解析模式
        print("parse param from model", flush=True)
        model_path = sysArgs.get("modelDir")
        output_file = sysArgs.get("outputFilePath")
        entry = sysArgs.get("entry")
        if model_path is None or output_file is None or entry is None:
            print(
                "model path and output file path and entry path is required",
                flush=True,
            )
            sys.exit(1)
        try:
            parse_param(model_path, output_file, entry)
        except Exception as e:
            writeOutputFile(
                output_file, json.dumps({"success": False, "message": e})
            )
    elif mode == "run":
        # 运行计算模式
        run_config_json_file = sysArgs.get("simConfig")
        if run_config_json_file is None:
            print("input file is required", flush=True)
            sys.exit(1)
        driver_path = sysArgs.get("driverConfig")
        if driver_path is None:
            driver_path = "./driver.config.json"
        output_file = sysArgs.get("outputFilePath")
        run(run_config_json_file, driver_path, output_file)
    else:
        print(f"unsupport sub command: ${mode}", flush=True)
        sys.exit(1)
